package msc.ai.mas.creditcard;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;

import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.lang.acl.UnreadableException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SealedObject;
import javax.crypto.SecretKey;

public class PaymentGatewayAgent extends Agent
{
	private HashMap<String, CreditCard> ccList;
	private HashMap<String, SealedObject> ccListEncrypt;
	private PaymentGatewayGui pgGui;
	
	private SecretKey key; // add by Saneth
	
	protected void setup() 
	{
		
		try {
			key = KeyGenerator.getInstance("DES").generateKey();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}// add by Saneth
		
		ccList = new HashMap<String, CreditCard>();
		ccListEncrypt = new HashMap<String, SealedObject>();// add by Saneth
		
		pgGui = new PaymentGatewayGui(this);
		pgGui.showGui();
		
		System.out.println(getAID().getLocalName() + ": Payment Gateway Started");
		DFAgentDescription dfd = new DFAgentDescription();
		dfd.setName(getAID());
		ServiceDescription sd = new ServiceDescription();
		sd.setType("payment-gateway");
		sd.setName("credit-card-authorization");
		dfd.addServices(sd);
		try 
		{
			DFService.register(this, dfd);
		}
		catch (FIPAException fe) 
		{
			fe.printStackTrace();
		}	
		
		addBehaviour(new CCAuthorizationBehavior());
	}
	
	protected void takeDown() 
	{
		
		try 
		{
			DFService.deregister(this);
		}
		catch (FIPAException fe) 
		{
			fe.printStackTrace();
		}
		
		this.pgGui.dispose();
		
		System.out.println("Payment-Gateway "+getAID().getName()+" terminating.");
	}
	
	public  HashMap<String, CreditCard> getCreditCardList()
	{
		return this.ccList;
	}
	
	public void addCreditCard(final CreditCard cc)
	{
		addBehaviour(new OneShotBehaviour() 
		{			
		
			@Override
			public void action()
			{
				System.out.println( getAID().getLocalName() + ": " + cc.getCreditCardNo()+ " card added."  );
				
				if (ccList == null ) ccList =  new HashMap<String, CreditCard>();
				
				ccList.put(cc.getCreditCardNo(),cc);
				
			}
			
		});
		
	}
	
	
	private class CCAuthorizationBehavior extends CyclicBehaviour
	{

		@Override
		public void action()
		{
			MessageTemplate mt = MessageTemplate.MatchPerformative(ACLMessage.REQUEST);
			ACLMessage msg = myAgent.receive(mt);
			if (msg != null) 
			{
				// someone looking for authorization.
				
				System.out.println(getAID().getLocalName() + ": CC Authorization Process Started");
				try
				{
					BuyingItem bItem = (BuyingItem) msg.getContentObject();
					System.out.println(getAID().getLocalName() + ": Authorizing... " + bItem.getCreditCardNo() 
										+ ", " + bItem.getCreditCVVNo() + ", " + bItem.getCreditCardType() );
					
					ACLMessage reply = msg.createReply();
					
					
					if (bItem != null)
					{
						CreditCard cc  = ccList.get(bItem.getCreditCardNo());
						if (cc != null)
						{
							if (cc.validateCCInfo(bItem.getCreditCardNo(), bItem.getCreditCVVNo(), bItem.getCreditCardType()))
							{
								int authCode = (int) Math.random();
								reply.setPerformative(ACLMessage.INFORM);
								reply.setContent("credit-card-atuhorized:" + authCode);
							}
							else
							{
								reply.setPerformative(ACLMessage.FAILURE);
								reply.setContent("credit-card-not-valid");
							}
							
							
						}
						else
						{
							reply.setPerformative(ACLMessage.FAILURE);
							reply.setContent("invalid-credit_card");
						}
						
					}
					else
					{
						reply.setPerformative(ACLMessage.FAILURE);
						reply.setContent("invalid-purchase-det");
					}
					
					myAgent.send(reply);
					System.out.println(getAID().getLocalName() + ": Reply Sent");
				} 
				catch (UnreadableException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			else
			{
				block();
			}
			
		}
		
	}
	
	// Add by Saneth
	public SealedObject getEncryptCreditCard(CreditCard creditCard)
			throws Exception {

		Cipher cipher = Cipher.getInstance("DES");
		cipher.init(Cipher.ENCRYPT_MODE, key);
		SealedObject sealedObject = new SealedObject(creditCard, cipher);
		return sealedObject;
	}

	// Add by Saneth
	public CreditCard getDecryptCreditCard(SealedObject sealedObject)
			throws Exception {

		// Prepare the decrypter
		Cipher dcipher = Cipher.getInstance("DES");
		dcipher.init(Cipher.DECRYPT_MODE, key);

		// Unseal (decrypt) the class
		CreditCard creditCard = (CreditCard) sealedObject.getObject(dcipher);
		return creditCard;

	}

	// Add by Saneth
	public String getEncryptCreditCardNumber(String creditCardNo)
			throws Exception {

		Cipher cipher = Cipher.getInstance("DES");
		cipher.init(Cipher.ENCRYPT_MODE, key);
		byte[] enCardNO = cipher.doFinal(creditCardNo.getBytes());
		String enCardNO_st = new String(enCardNO);
		return enCardNO_st;
	}

	// Add by Saneth
	public String getDecryptCreditCardNumber(String creditCardNoD)
			throws Exception {

		// Prepare the decrypter
		Cipher dcipher = Cipher.getInstance("DES");
		dcipher.init(Cipher.DECRYPT_MODE, key);
		byte[] recoveredBytes = dcipher.doFinal(creditCardNoD.getBytes());
		return new String(recoveredBytes);

	}

}
